#ifndef _K_STRING_
#define _K_STRING_

#include "assert.h"

namespace krt
{
	template<typename T>
	size_t find_zero(const T* buf)
	{
		if (!buf)
			return 0;

		size_t result = 0;

		while (buf[result] != 0)
			++result;

		return result;
	}

	template<typename T>
	size_t find_zero_s(const T* buf, size_t buflen)
	{
		if (!buf)
			return 0;

		if (!buf)
			return 0;

		size_t result = 0;

		while (result < buflen && buf[result] != 0)
			++result;

		return result;
	}

	template<typename T>
	class CountedBuffer
	{
	private:
		const T *m_str;
		size_t m_len;
	public:
		CountedBuffer(const T* str, size_t len)
		{
			m_str = str;
			m_len = len;
		}

		inline const T* GetBuffer() const
		{
			return m_str;
		}

		inline size_t GetLength() const
		{
			return m_len;
		}

		inline operator const T*() const
		{
			return m_str;
		}
	};

	template<>
	class CountedBuffer<char>
	{
	private:
		const char *m_str;
		size_t m_len;
	public:
		CountedBuffer(const char* str, size_t len)
		{
			m_str = str;
			m_len = len;
		}

		CountedBuffer(const char* str)
		{
			m_str = str;

			m_len = strlen(str);
		}

		inline const char* GetBuffer() const
		{
			return m_str;
		}

		inline size_t GetLength() const
		{
			return m_len;
		}

		inline operator const char*() const
		{
			return m_str;
		}
	};

	template<>
	class CountedBuffer<wchar_t>
	{
	private:
		const wchar_t *m_str;
		size_t m_len;
	public:
		CountedBuffer(const wchar_t* str, size_t len)
		{
			m_str = str;
			m_len = len;
		}

		CountedBuffer(const wchar_t* str)
		{
			m_str = str;

			m_len = wcslen(str);
		}

		inline const wchar_t* GetBuffer() const
		{
			return m_str;
		}

		inline size_t GetLength() const
		{
			return m_len;
		}

		inline operator const wchar_t*() const
		{
			return m_str;
		}
	};

	template<typename T, size_t L>
	class StaticArray
	{
	private:
		T m_str[L + 1];
		size_t m_len;
	public:
		StaticArray(const T* str, size_t len)
		{
			assign(str, len);
		}

		StaticArray()
		{
			m_len = 0;
			m_str[0] = 0;
		}

		StaticArray(const StaticArray &src)
		{
			assign(src.GetBuffer(), src.GetLength());
		}

		StaticArray(const CountedBuffer<T> &str)
		{
			assign(str.GetBuffer(), str.GetLength());
		}

		void assign(const T* str, size_t len)
		{
			if (len > L)
				m_len = L;
			else 
				m_len = len;

			if (m_len)
				memcpy(m_str, str, sizeof(T) * m_len);

			m_str[m_len] = 0;
		}

		inline const T* GetBuffer() const
		{
			return m_str;
		}

		inline size_t GetLength() const
		{
			return m_len;
		}

		inline operator T*()
		{
			return m_str;
		}

		StaticArray& operator=(const StaticArray &src)
		{
			assign(src.GetBuffer(), src.GetLength());
		}

		void append(const T* tail, size_t len)
		{
			size_t rem = L - m_len;

			if (len > rem)
				assert(false);
			else
			{
				memcpy(m_str + m_len, tail, len * sizeof(T));

				m_len += len;

				m_str[m_len] = 0;
			}
		}

		void append(const CountedBuffer<T> &tail)
		{
			append(tail.GetBuffer(), tail.GetLength());
		}

		inline void SetLength(size_t len)
		{
			if (len <= L)
			{
				m_len = len;
				m_str[m_len] = 0;
			}
		}
	};

	template<size_t L>
	class StaticArray<char, L>
	{
	private:
		char m_str[L + 1];
		size_t m_len;
	public:
		StaticArray(const char* str, size_t len)
		{
			assign(str, len);
		}

		StaticArray(const char* str)
		{
			assign(str, find_zero(str));
		}

		StaticArray()
		{
			m_len = 0;
			m_str[0] = 0;
		}

		StaticArray(const StaticArray &src)
		{
			assign(src.GetBuffer(), src.GetLength());
		}

		StaticArray(const CountedBuffer<char> &str)
		{
			assign(str.GetBuffer(), str.GetLength());
		}

		void assign(const char* str, size_t len)
		{
			if (len > L)
				m_len = L;
			else 
				m_len = len;

			if (m_len)
				memcpy(m_str, str, sizeof(char) * m_len);

			m_str[m_len] = 0;
		}

		inline const char* GetBuffer() const
		{
			return m_str;
		}

		inline size_t GetLength() const
		{
			return m_len;
		}

		inline operator char*()
		{
			return m_str;
		}

		StaticArray& operator=(const char* str)
		{
			assign(str, strlen(str));

			return *this;
		}

		StaticArray& operator=(const StaticArray &src)
		{
			assign(src.GetBuffer(), src.GetLength());
		}

		void append(const char* tail, size_t len)
		{
			size_t rem = L - m_len;

			if (len > rem)
				assert(false);
			else
			{
				memcpy(m_str + m_len, tail, len * sizeof(char));

				m_len += len;

				m_str[m_len] = 0;
			}
		}

		void append(const CountedBuffer<char> &tail)
		{
			append(tail.GetBuffer(), tail.GetLength());
		}

		void AppendPath(const char* buf, size_t len)
		{
			if (!buf || !len)
				return;

			if (IS_PATH_DELIMITER(buf[0]))
			{
				++buf;
				--len;
			}

			if (IS_PATH_DELIMITER(buf[len - 1]))
				--len;

			if (!len)
				return;

			BOOL flag;

			if (m_len && !IS_PATH_DELIMITER(m_str[m_len - 1]))
			{
				m_str[m_len++] = PATH_DELIMITER;
				flag = true;
			}
			else flag = false;

			size_t rem = L - m_len;

			if (len > rem)
			{
				if (flag)
					m_str[--m_len] = 0;
				return;
			}

			memcpy(m_str + m_len, buf, len * sizeof(char));

			m_len += len;

			m_str[m_len] = 0;
		}

		void AppendPath(const CountedBuffer<char> &tail)
		{
			AppendPath(tail.GetBuffer(), tail.GetLength());
		}

		inline void SetLength(size_t len)
		{
			if (len <= L)
			{
				m_len = len;
				m_str[m_len] = 0;
			}
		}

		void GetSubDir(StaticArray *subdir, BOOL exclude = false)
		{
			if (!m_len)
			{
				if (subdir)
					subdir->SetLength(0);

				return;
			}

			if (IS_PATH_DELIMITER(m_str[m_len - 1]))
			{
				m_str[m_len - 1] = 0;
				--m_len;
			}

			size_t i = m_len;

			while(i && !IS_PATH_DELIMITER(m_str[i - 1])) --i;

			if (i)
			{
				if (subdir)
					subdir->assign(m_str + i, m_len - i);

				if (exclude)
					SetLength(--i);
			}
			else if (subdir)
				subdir->SetLength(0);
		}

		void ExtractSubDir(StaticArray &subdir)
		{
			GetSubDir(&subdir, true);
		}

		StaticArray& ParentDir()
		{
			GetSubDir(NULL, true);
			return (*this);
		}

	};

	template<size_t L>
	class StaticArray<wchar_t, L>
	{
	private:
		wchar_t m_str[L + 1];
		size_t m_len;
	public:
		StaticArray(const wchar_t* str, size_t len)
		{
			assign(str, len);
		}

		StaticArray(const wchar_t* str)
		{
			assign(str, wcslen(str));
		}

		StaticArray()
		{
			m_len = 0;
			m_str[0] = 0;
		}

		StaticArray(const StaticArray &src)
		{
			assign(src.GetBuffer(), src.GetLength());
		}

		StaticArray(const CountedBuffer<wchar_t> &str)
		{
			assign(str.GetBuffer(), str.GetLength());
		}

		void assign(const wchar_t* str, size_t len)
		{
			if (len > L)
				m_len = L;
			else 
				m_len = len;

			if (m_len)
				memcpy(m_str, str, sizeof(wchar_t) * m_len);

			m_str[m_len] = 0;
		}

		inline const wchar_t* GetBuffer() const
		{
			return m_str;
		}

		inline size_t GetLength() const
		{
			return m_len;
		}

		inline operator wchar_t*()
		{
			return m_str;
		}

		StaticArray& operator=(const wchar_t* str)
		{
			assign(str, wcslen(str));

			return *this;
		}

		StaticArray& operator=(const StaticArray &src)
		{
			assign(src.GetBuffer(), src.GetLength());
		}

		void append(const wchar_t* tail, size_t len)
		{
			size_t rem = L - m_len;

			if (len > rem)
				assert(false);
			else
			{
				memcpy(m_str + m_len, tail, len * sizeof(wchar_t));

				m_len += len;

				m_str[m_len] = 0;
			}
		}

		void append(const CountedBuffer<wchar_t> &tail)
		{
			append(tail.GetBuffer(), tail.GetLength());
		}

		void AppendPath(const wchar_t* buf, size_t len)
		{
			if (!buf || !len)
				return;

			if (IS_PATH_DELIMITER(buf[0]))
			{
				++buf;
				--len;
			}

			if (IS_PATH_DELIMITER(buf[len - 1]))
				--len;

			if (!len)
				return;

			BOOL flag;

			if (m_len && !IS_PATH_DELIMITER(m_str[m_len - 1]))
			{
				m_str[m_len++] = PATH_DELIMITER;
				flag = true;
			}
			else flag = false;

			size_t rem = L - m_len;

			if (len > rem)
			{
				if (flag)
					m_str[--m_len] = 0;
				return;
			}

			memcpy(m_str + m_len, buf, len * sizeof(wchar_t));

			m_len += len;

			m_str[m_len] = 0;
		}

		void AppendPath(const CountedBuffer<wchar_t> &tail)
		{
			AppendPath(tail.GetBuffer(), tail.GetLength());
		}

		inline void SetLength(size_t len)
		{
			if (len <= L)
			{
				m_len = len;
				m_str[m_len] = 0;
			}
		}

		void GetSubDir(StaticArray *subdir, BOOL exclude = false)
		{
			if (!m_len)
			{
				if (subdir)
					subdir->SetLength(0);

				return;
			}

			if (IS_PATH_DELIMITER(m_str[m_len - 1]))
			{
				m_str[m_len - 1] = 0;
				--m_len;
			}

			size_t i = m_len;

			while(i && !IS_PATH_DELIMITER(m_str[i - 1])) --i;

			if (i)
			{
				if (subdir)
					subdir->assign(m_str + i, m_len - i);

				if (exclude)
					SetLength(--i);
			}
			else if (subdir)
				subdir->SetLength(0);
		}

		void ExtractSubDir(StaticArray &subdir)
		{
			GetSubDir(&subdir, true);
		}

		StaticArray& ParentDir()
		{
			GetSubDir(NULL, true);
			return (*this);
		}

	};

	template <typename T>
	class DynamicArray
	{
	private:
		typedef struct DynamicArray_data
		{
			LONG nReference;
			size_t nElement;
			T* pData;
		}DynamicArray_data;

		DynamicArray_data *m_data;

	protected:

		inline LONG AddRef()
		{
			if (m_data)
				return InterlockedIncrement(&(m_data->nReference));
			else
				return 0;
		}

		inline void release()
		{
			if (m_data)
			{
				if (!InterlockedDecrement(&(m_data->nReference)))
				{
					if (m_data->pData)
						delete [] (m_data->pData);

					free(m_data);

					m_data = NULL;
				}
			}
		}

		inline T* GetWritableBuffer() const
		{
			if (m_data)
				return m_data->pData;
			else
				return NULL;
		}

	public:
		class DynamicArray_element
		{
		private:
			DynamicArray *m_owner;
			size_t m_index;
		public:
			DynamicArray_element(DynamicArray * str, size_t index)
			{
				m_owner = str;
				m_index = index;
			};

			inline operator T()
			{
				return m_owner->ElementAt(m_index); 
			}

			inline void operator=(const T& c)
			{
				m_owner->ElementAt(m_index, c);
			}

			inline T operator*()
			{
				return m_owner->ElementAt(m_index); 
			}

			inline T value()
			{
				return m_owner->ElementAt(m_index); 
			}
		};

	public:
		DynamicArray():m_data(NULL){}

		DynamicArray(const DynamicArray& src)
		{
			m_data = src.m_data;
			AddRef();	
		}

		DynamicArray(const T* s): m_data(NULL)
		{   
			if (s)
				SetBuffer(s, find_zero(s));
			else
				SetBuffer(NULL, 0);
		}

		DynamicArray(const T* s, size_t size):m_data(NULL)
		{
			SetBuffer(s, size);
		}

		virtual ~DynamicArray()
		{
			release();
		}

		inline const T* GetBuffer() const 
		{
			if (m_data)
				return m_data->pData;
			else
				return NULL;
		}

		inline size_t GetLength() const 
		{
			if (m_data)
				return m_data->nElement;
			else 
				return 0;
		}

		inline T ElementAt(size_t index) const
		{
			return GetBuffer()[index];
		}

		inline void ElementAt(size_t index, const T& c)
		{
			const T* s = GetBuffer();

			if (s[index] != c)
			{
				if (m_data->nReference != 1)
					*this = DynamicArray(s, GetLength());

				m_data->pData[index] = c;
			}
		}

		inline void CheckIndex(size_t index) const
		{
			size_t size = GetLength();
			if (index >= size)
			{
				char msg[256];
				sprintf_s(msg, "GetLength=%u, index=%u", array_length(msg), size, index);
				throw msg;
			}
		}

		inline DynamicArray_element operator[](size_t index) 
		{		
			CheckIndex(index);
			return DynamicArray_element(this, index);
		}

		void SetBuffer(const T* s, size_t size)
		{
			T* buf = GetWritableBuffer();

			if (s && size && s != buf)
			{
				if (GetLength() == size && 1 == m_data->nReference)
				{
					for(size_t i = 0; i < size; ++i)
						buf[i] = s[i];
				}
				else
				{
					release();
					m_data = new DynamicArray_data;
					m_data->nReference = 1;
					m_data->nElement = size;
					buf = new T[size + 1];
					m_data->pData = buf;

					for(size_t i = 0; i < size; ++i)
						buf[i] = s[i];

					buf[size] = 0;
				}
			}
		}

		void append(const T* s, size_t size)
		{
			if (s && size)
			{
				size_t mysize = GetLength();

				T* buf = new T[size + mysize + 1];

				size_t i;

				for(i = 0; i < mysize; ++i)
					buf[i] = m_data->pData[i];

				for(i = 0; i < size; ++i)
					buf[i + mysize] = s[i];

				buf[size + mysize] = 0;

				if (m_data)
				{
					if (m_data->nReference == 1)
						delete [] (m_data->pData);
					else
					{
						release();
						m_data = new DynamicArray_data();
					}
				}
				else
					m_data = new DynamicArray_data();


				m_data->nReference = 1;
				m_data->nElement = size + mysize;
				m_data->pData = buf;
			}
		}

		DynamicArray& operator=(const DynamicArray &src)
		{
			release();
			m_data = src.m_data;
			AddRef();
			return (*this);
		}

		DynamicArray& operator=(const T* s)
		{
			if (s)
				SetBuffer(s, find_zero(s));
			else 
				SetBuffer(s, 0);

			return (*this);
		}

		DynamicArray operator+(const DynamicArray &tail)
		{
			DynamicArray tmp(*this);
			tmp.append(tail.GetBuffer(), tail.GetLength());
			return tmp;
		}

		DynamicArray& operator+=(const DynamicArray &tail)
		{
			DynamicArray tmp(*this);
			tmp.append(tail.GetBuffer(), tail.GetLength());
			(*this) = tmp;
			return (*this);
		}

		DynamicArray operator+(const T* s)
		{
			DynamicArray tmp(*this);
			append(s, find_zero(s));
			return tmp;
		}

		DynamicArray& operator+=(const T* s)
		{
			DynamicArray tmp(*this);
			tmp.append(s, find_zero(s));
			(*this) = tmp;
			return (*this);
		}

		DynamicArray copy(size_t StartIndex, size_t size) const
		{
			size_t mysize = GetLength();

			if (StartIndex >= mysize)
				return DynamicArray();

			if (size > mysize - StartIndex)
				size = mysize - StartIndex;

			const char* T = GetBuffer();
			return DynamicArray(s + StartIndex, size);
		}

		DynamicArray& remove(size_t StartIndex, size_t size)
		{
			size_t mysize = GetLength();

			if (StartIndex < mysize)
			{
				if (size > mysize - StartIndex)
					size = mysize - StartIndex;

				if (size == mysize)
					release();

				const T* s = GetBuffer();

				T* buf = new T[mysize - size + 1];

				size_t i, j;

				for(i = 0; i < StartIndex; ++i)
					buf[i] = s[i];

				j = StartIndex + size;

				for(; i < mysize - size; ++i, ++j)
					buf[i] = s[j];

				SetBuffer(buf, mysize - size);
			}

			return (*this);
		}

	};
}

#endif	//_K_STRING_